# -*- coding: utf-8 -*-
"""
@Time： 2023/12/20 16:20
@Auth： gubei
@File： logger.py
@IDE：PyCharm
@Description：日志工具类
"""
import inspect
import os
import sys
import time
import traceback
from typing import Any, Dict, Optional, Union, Callable
from pathlib import Path

from loguru import logger

from env import setting


# 获取项目根目录
project_root = Path(__file__).parent.parent
# 日志目录
LOG_DIR = project_root / "logs"

# 使用日期作为日志文件标识
PROCESS_ID = "{time:YYYY-MM-DD}"

# @SingletonDecorator
class Log:
    """
    日志记录器类
    
    用于统一记录应用日志，自动提取调用位置信息
    """
    business: str

    def __init__(self, name: str = 'yhpc'):  
        """
        初始化日志记录器
        
        Args:
            name: 业务名称标识
        """
        # 如果目录不存在则创建
        if not os.path.exists(LOG_DIR):
            os.makedirs(LOG_DIR, exist_ok=True)
        self.business = name

    def _get_caller_info(self) -> Dict[str, Any]:
        """
        获取调用者信息
        
        Returns:
            包含调用者文件名、行号、函数名的字典
        """
        frame = inspect.currentframe().f_back.f_back
        info = inspect.getframeinfo(frame)
        return {
            "filename": info.filename,
            "line": info.lineno,
            "func": info.function,
            "business": self.business
        }

    def info(self, message: Union[str, Any]):
        """
        记录信息级别日志
        
        Args:
            message: 日志消息
        """
        caller_info = self._get_caller_info()
        logger.bind(
            name=setting.PITY_INFO if hasattr(setting, 'PITY_INFO') else "INFO", 
            **caller_info
        ).info(message)

    def error(self, message: Union[str, Any], exc_info: bool = False):
        """
        记录错误级别日志
        
        Args:
            message: 错误消息
            exc_info: 是否包含异常信息
        """
        caller_info = self._get_caller_info()
        log = logger.bind(
            name=setting.PITY_ERROR if hasattr(setting, 'PITY_ERROR') else "ERROR", 
            **caller_info
        )
        
        if exc_info:
            log.exception(message)
        else:
            log.error(message)

    def warning(self, message: Union[str, Any]):
        """
        记录警告级别日志
        
        Args:
            message: 警告消息
        """
        caller_info = self._get_caller_info()
        logger.bind(
            name=setting.PITY_ERROR if hasattr(setting, 'PITY_ERROR') else "WARNING", 
            **caller_info
        ).warning(message)

    def debug(self, message: Union[str, Any]):
        """
        记录调试级别日志
        
        Args:
            message: 调试消息
        """
        caller_info = self._get_caller_info()
        logger.bind(
            name=setting.PITY_INFO if hasattr(setting, 'PITY_INFO') else "DEBUG", 
            **caller_info
        ).debug(message)

    def exception(self, message: Union[str, Any], exc_info: Optional[Exception] = None):
        """
        记录异常信息日志
        
        Args:
            message: 异常消息
            exc_info: 异常对象，默认为当前异常
        """
        caller_info = self._get_caller_info()
        logger.bind(
            name=setting.PITY_ERROR if hasattr(setting, 'PITY_ERROR') else "EXCEPTION", 
            **caller_info
        ).exception(message)
        
    def critical(self, message: Union[str, Any]):
        """
        记录严重错误级别日志
        
        Args:
            message: 严重错误消息
        """
        caller_info = self._get_caller_info()
        logger.bind(
            name=setting.PITY_ERROR if hasattr(setting, 'PITY_ERROR') else "CRITICAL", 
            **caller_info
        ).critical(message)
        
    def success(self, message: Union[str, Any]):
        """
        记录成功操作日志
        
        Args:
            message: 成功操作消息
        """
        caller_info = self._get_caller_info()
        logger.bind(
            name=setting.PITY_INFO if hasattr(setting, 'PITY_INFO') else "SUCCESS", 
            **caller_info
        ).success(message)
        
    def trace(self, message: Union[str, Any]):
        """
        记录追踪级别日志
        
        Args:
            message: 追踪消息
        """
        caller_info = self._get_caller_info()
        logger.bind(
            name=setting.PITY_INFO if hasattr(setting, 'PITY_INFO') else "TRACE", 
            **caller_info
        ).trace(message)

    @staticmethod
    def configure_logger():
        """
        配置全局日志设置
        
        添加日志处理器，设置日志格式和输出位置
        """
        # 检查是否已经有配置的日志处理器
        # 如果有超过1个处理器，说明已经由core/log配置过了
        if len(logger._core.handlers) > 1:
            return
            
        # 清除所有处理器
        logger.remove()
        
        # 添加控制台处理器
        logger.add(
            sys.stderr,
            format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
            level="DEBUG" if not setting.APP_MODE else "INFO",
            colorize=True,
        )
        
        # 确保日志目录存在
        os.makedirs(LOG_DIR, exist_ok=True)
        
        # 使用日期作为文件名，而不是进程ID
        # 添加文件处理器 - 所有日志
        logger.add(
            os.path.join(LOG_DIR, "utils_logger_{time:YYYY-MM-DD}.log"),
            format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} - {message}",
            level="DEBUG",
            rotation="00:00",  # 每天午夜创建新文件
            retention="7 days",  # 保留7天
            compression="zip",  # 压缩旧日志
            encoding="utf-8",
            enqueue=True,  # 启用线程安全
        )
        
        # 添加文件处理器 - 仅错误日志
        logger.add(
            os.path.join(LOG_DIR, "utils_error_{time:YYYY-MM-DD}.log"),
            format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} - {message}",
            level="ERROR",
            rotation="00:00",
            retention="7 days",
            compression="zip",
            encoding="utf-8",
            enqueue=True,  # 启用线程安全
        )
